#include "uheaderview.h"
#include "style/ustylepainter.h"
#include "uapplication.h"
#include "style/upalette.h"

#include <QMouseEvent>
#include <QPainter>
#include <QPainterPath>
#include <QStyleOptionHeader>

class UHeaderViewPrivate
{
public:
    UHeaderViewPrivate(UHeaderView* q) : q_ptr(q) {}
    ~UHeaderViewPrivate() {}

    void init();
    QRect checkBoxRect(const QRect &rect, const QSize &size) const;

private:
    Q_DECLARE_PUBLIC(UHeaderView)
    UHeaderView* const q_ptr = nullptr;
    int checkBoxColumn = -1;
    Qt::CheckState state = Qt::CheckState::Unchecked;
    bool isChecked = false;
    bool isDown = false;
    bool isHover = false;
    int margins = 3;
    QMap<int, QWidget*> headeritems;
};

void UHeaderViewPrivate::init()
{
    Q_Q(UHeaderView);

    q->setMouseTracking(true);
    q->viewport()->setMouseTracking(true);

    q->setFrameShape(QFrame::NoFrame);
    q->setEditTriggers(QAbstractItemView::NoEditTriggers);
    q->setTextElideMode(Qt::TextElideMode::ElideMiddle);
    q->viewport()->setAutoFillBackground(false);
}

QRect UHeaderViewPrivate::checkBoxRect(const QRect &rect, const QSize &size) const
{
    return QRect(rect.x() + (rect.width() - size.width()) / 2,
                 rect.y() + (rect.height() - size.height()) / 2, size.width(), size.height());
}

UHeaderView::UHeaderView(Qt::Orientation orientation, QWidget* parent, int checkBoxColumn)
    : QHeaderView(orientation, parent)
    , d_ptr(new UHeaderViewPrivate(this))
{
    Q_D(UHeaderView);

    d->init();
    d->checkBoxColumn = checkBoxColumn;
    connect(this, &UHeaderView::sectionResized, this, &UHeaderView::onSectionResized);
}

UHeaderView::~UHeaderView()
{
    Q_D(UHeaderView);

    d->headeritems.clear();
    delete d;
}

void UHeaderView::updateContent()
{
    updateGeometries();
}

void UHeaderView::setCheckColumnIndex(int index)
{
    Q_D(UHeaderView);
    d->checkBoxColumn = index;
}

int UHeaderView::checkColumnIndex()
{
    Q_D(UHeaderView);
    return d->checkBoxColumn;
}

void UHeaderView::updateHeaderViewSection(int logicalIndex)
{
    updateSection(logicalIndex);
}

Qt::CheckState UHeaderView::checkState() const
{
    Q_D(const UHeaderView);
    return d->state;
}

void UHeaderView::setCheckState(Qt::CheckState state)
{
    Q_D(UHeaderView);
    if (d->state != state)
    {
        d->state = state;
        if (Qt::CheckState::Checked == state)
        {
            d->isChecked = true;
        }
        else if (Qt::CheckState::Unchecked == state)
        {
            d->isChecked = false;
        }
        updateSection(d->checkBoxColumn);
    }
}

void UHeaderView::setIndexWidget(int index, QWidget* item)
{
    Q_D(UHeaderView);
    if (!d->headeritems.contains(index))
    {
        d->headeritems.insert(index, item);
    }
}

const QMap<int, QWidget*> &UHeaderView::headerItemMap()
{
    Q_D(UHeaderView);
    return d->headeritems;
}

void UHeaderView::onSectionResized(int logicalIndex, int oldSize, int newSize)
{
    Q_D(UHeaderView);
    QWidget* item = d->headeritems[logicalIndex];
    if (nullptr != item)
    {
        item->setFixedWidth(newSize);
    }
}

void UHeaderView::initStyleOption(QStyleOptionButton* option) const
{
    if (!option)
        return;
    Q_D(const UHeaderView);

    option->state |= QStyle::State_Enabled;
    if (d->isDown)
        option->state |= QStyle::State_Sunken;
    if (Qt::CheckState::PartiallyChecked == d->state)
        option->state |= QStyle::State_NoChange;
    else
        option->state |= d->isChecked ? QStyle::State_On : QStyle::State_Off;

    option->state.setFlag(QStyle::State_MouseOver, d->isHover);
}

void UHeaderView::paintSection(QPainter* painter, const QRect &rect, int logicalIndex) const
{
    Q_D(const UHeaderView);
    QWidget* item = d->headeritems[logicalIndex];
    if (nullptr != item)
    {
        item->setGeometry(rect);
    }
    else
    {
        paintSectionContent(painter, rect, logicalIndex);
    }

    if (logicalIndex == d->checkBoxColumn)
    {
        QStyleOptionButton option;
        initStyleOption(&option);
        option.rect = d->checkBoxRect(rect, QSize(16, 16));
        UStylePainter stylePainter(this->viewport());
        stylePainter.drawControl(UProxyStyle::UControlElement::CE_CheckBox, option);
    }
}

void UHeaderView::paintSectionContent(QPainter* painter, const QRect &rect, int logicalIndex) const
{
    Q_D(const UHeaderView);
    painter->save();
    painter->setRenderHint(QPainter::Antialiasing);

    QStyleOptionHeader option;
    QHeaderView::initStyleOption(&option);
    QPalette::ColorGroup cg = UProxyStyle::colorGroup(&option, viewport());
    UPalette* palette = uApp->applicationPalette();
    QBrush contentBrush = palette->brush(cg, QPalette::Base);
    painter->fillRect(rect, contentBrush);

    int margin = 10;
    int spacing = 1;
    int kSpacingMargin = 4;
    // title
    QRect contentRect(rect.x(), rect.y(), rect.width(), rect.height() - spacing);
    QRect hSpacingRect(rect.x(), contentRect.height(), rect.width(),
                       rect.height() - contentRect.height());
    // horizontal spacing
    QBrush hSpacingBrush(palette->color(cg, UPalette::SplitLineBackground));
    // vertical spacing
    QBrush vSpacingBrush(palette->color(cg, UPalette::SplitLineBackground));

    QRectF vSpacingRect(rect.x(), rect.y() + kSpacingMargin, spacing,
                        rect.height() - kSpacingMargin * 2);

    QBrush clearBrush(palette->color(cg, QPalette::Window));

    painter->fillRect(hSpacingRect, clearBrush);
    painter->fillRect(hSpacingRect, hSpacingBrush);

    if (visualIndex(logicalIndex) > 0)
    {
        painter->fillRect(vSpacingRect, clearBrush);
        painter->fillRect(vSpacingRect, vSpacingBrush);
    }

    if (logicalIndex == d->checkBoxColumn)
    {
        painter->restore();
        return;
    }

    QPen forground;
    forground.setColor(palette->color(cg, QPalette::Text));
    QRect textRect;
    if (logicalIndex == sortIndicatorSection())
    {
        textRect = {contentRect.x() + margin, contentRect.y(), contentRect.width() - margin * 3 - 8,
                    contentRect.height()
                   };
    }
    else
    {
        textRect = {contentRect.x() + margin, contentRect.y(), contentRect.width() - margin,
                    contentRect.height()
                   };
    }
    if (!model())
    {
        return;
    }
    QString title = model()->headerData(logicalIndex, orientation(), Qt::DisplayRole).toString();
    int align = Qt::AlignLeft | Qt::AlignVCenter;

    painter->setPen(forground);
    QFontMetrics fm(this->font());
    QString text = fm.elidedText(title, textElideMode(), textRect.width());
    painter->drawText(textRect, static_cast<int>(align), text);

    if (isSortIndicatorShown() && logicalIndex == sortIndicatorSection())
    {
        QRect sortIndicator(textRect.x() + textRect.width() + margin,
                            textRect.y() + (textRect.height() - 5) / 2, 8, 10);
        option.rect = sortIndicator;
        if (sortIndicatorOrder() == Qt::DescendingOrder)
        {
            style()->drawPrimitive(QStyle::PE_IndicatorArrowDown, &option, painter, this);
        }
        else if (sortIndicatorOrder() == Qt::AscendingOrder)
        {
            style()->drawPrimitive(QStyle::PE_IndicatorArrowUp, &option, painter, this);
        }
    }
    painter->restore();
}

void UHeaderView::mousePressEvent(QMouseEvent* e)
{
    Q_D(UHeaderView);
    int logicalIndex = logicalIndexAt(e->pos());
    if ((e->buttons() & Qt::LeftButton) && (logicalIndex == d->checkBoxColumn))
    {
        QRect rect = QRect(sectionViewportPosition(logicalIndex), 0, sectionSize(logicalIndex), height());
        QRect checkBoxRect = d->checkBoxRect(rect, QSize(16, 16));
        d->isDown = checkBoxRect.contains(e->pos());
        updateSection(logicalIndex);
    }
    else
    {
        QHeaderView::mousePressEvent(e);
    }
}

void UHeaderView::mouseMoveEvent(QMouseEvent* e)
{
    Q_D(UHeaderView);
    int logicalIndex = logicalIndexAt(e->pos());
    if (logicalIndex == d->checkBoxColumn)
    {
        QRect rect = QRect(sectionViewportPosition(logicalIndex), 0, sectionSize(logicalIndex), height());
        QRect checkBoxRect = d->checkBoxRect(rect, QSize(16, 16));
        d->isHover = checkBoxRect.contains(e->pos());
        updateSection(logicalIndex);
    }
    QHeaderView::mouseMoveEvent(e);
}

void UHeaderView::mouseReleaseEvent(QMouseEvent* e)
{
    Q_D(UHeaderView);
    if (d->isDown)
    {
        d->isChecked = !d->isChecked;
        d->state = d->isChecked ? Qt::CheckState::Checked : Qt::CheckState::Unchecked;
        d->isDown = false;
        int index = logicalIndexAt(e->pos());
        updateSection(index);
        emit stateChanged(d->state, index);
    }
    QHeaderView::mouseReleaseEvent(e);
}
